package calculation;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import my_interface.FrequenceType;
import my_interface.SelectType;

/**
 *
 * @author KieuOanh
 */
public class FIRFrequenceSample implements SelectType, FrequenceType {

    public int typeFrequence;
    public int typeSelect;
    //
    public double wp;
    public double ws;
    public double As;
    public double Rp;
    //
    public double w2p;
    public double w2s;
    //
    public double alpha;
    public double theta;
    public final double pi = Math.PI;
    public final double pi2 = 2 * Math.PI;
    //
    public int M;
    public int k;
    public int kMax;
    public int kMin;
    //
    public double T1;
    public double T2;
    public double[] wk;
    public double wx;
    public double hnMax = 0;
    public double hnMin = 0;
    public double HwMax = 0;
    public double HwMin = 0;
    public double dBMax = 0;
    public double dBMin = 0;
    public double HkMax = 0;
    //
    public double real1;
    public double real2;
    public double imag1;
    public double imag2;
    //
    int k1, k2, k3, k4;
    //
    public double[] hd;
    public double[] hn;
    public double[] w;
    public double[] dB;
    public double[] Hw;
    public double[] Hk;
    public final double eps = 1.0 / 5.0 + 1.0 / 5.0 - 1.0 / 10.0 - 1.0 / 10.0 - 1.0 / 10.0 - 1.0 / 10.0;

    public FIRFrequenceSample(int typeSelect, int typeFrequence, int M, double wp, double ws, double As, double Rp, double T1, double T2) {
        this.typeFrequence = typeFrequence;
        this.typeSelect = typeSelect;
        this.wp = wp;
        this.ws = ws;
        this.As = As;
        this.Rp = Rp;
        this.M = M;
        this.T1 = T1;
        this.T2 = T2;

        alpha = (double) (M - 1) / 2;

        contructArrayH(M);

        k1 = round(wp * M / 2);
        k2 = round(ws * M / 2);


        System.out.println("k1= " + k1);

        if (M % 2 == 0) {
            for (int k = 0; k <= M / 2; k++) {
                switch (typeSelect) {
                    case LOW_PASS:
                        if (k <= k1) {
                            Hk[k] = 1;
                        } else {
                            Hk[k] = 0;
                        }
                        break;
                    case HIGH_PASS:
                        if (k >= k1) {
                            Hk[k] = 1;
                        } else {
                            Hk[k] = 0;
                        }
                        break;
                }
                if (k > 0) {
                    Hk[M - k] = Hk[k];
                }
            }
        } else {
            for (int k = 0; k <= M / 2 + 1; k++) {
                switch (typeSelect) {
                    case LOW_PASS:
                        if (k <= k1) {
                            Hk[k] = 1;
                        } else {
                            Hk[k] = 0;
                        }
                        break;
                    case HIGH_PASS:
                        if (k >= k1) {
                            Hk[k] = 1;
                        } else {
                            Hk[k] = 0;
                        }
                        break;
                }
                if (k > 0) {
                    Hk[M - k] = Hk[k];
                }
            }
        }

        wp *= pi;
        ws *= pi;

        if (typeFrequence == OPTIMUM_DESIGN) {
            findKMaxMin();
            switch (typeSelect) {
                case LOW_PASS:
                    System.out.println("Kmax= " + kMax);
                    if (T1 > 0 && T1 < 1 && (T2 == 0)) {
                        Hk[kMax + 1] = T1;
                        Hk[M - kMax - 1] = T1;
                    } else if ((T1 > 0 && T1 < 1) && (T2 > 0 && T2 < 1)) {
                        Hk[kMax + 1] = T1;
                        Hk[kMax + 2] = T2;
                        Hk[M - kMax - 1] = T1;
                        Hk[M - kMax - 2] = T2;
                    }
                    break;
                case HIGH_PASS:
                    System.out.println("Kmin= " + kMin);
                    if (T1 > 0 && T1 < 1 && (T2 == 0)) {
                        Hk[kMin - 1] = T1;
                        Hk[M - kMin + 1] = T1;
                    } else if ((T1 > 0 && T1 < 1) && (T2 > 0 && T2 < 1)) {
                        Hk[kMin - 1] = T2;
                        Hk[kMin - 2] = T1;
                        Hk[M - kMin + 1] = T2;
                        Hk[M - kMin + 2] = T1;
                    }
                    break;
            }

        }


        for (int k = 0; k < M; k++) {
            System.out.println("Hk[" + k + "]" + Hk[k]);
        }

        setValuehn();
        setValueDB(M);
        findMaxMin();
    }

    public FIRFrequenceSample(int typeSelect, int typeFrequence, int M, double wp, double ws, double As, double Rp, double w2p, double w2s, double T1, double T2) {
        this.typeFrequence = typeFrequence;
        this.typeSelect = typeSelect;
        this.wp = wp;
        this.ws = ws;
        this.As = As;
        this.Rp = Rp;
        this.w2p = w2p;
        this.w2s = w2s;
        this.M = M;
        this.T1 = T1;
        this.T2 = T2;

        alpha = (double) (M - 1) / 2;

        contructArrayH(M);

        k1 = round(wp * M / 2);
        k2 = round(ws * M / 2);
        k3 = round(w2p * M / 2);
        k4 = round(w2s * M / 2);

        for (int k = 0; k <= (M - 1) / 2; k++) {
            switch (typeSelect) {
                case BAND_PASS:
                    if (k > k2 && k < k4) {
                        Hk[k] = 1;
                    } else {
                        Hk[k] = 0;
                    }
                    break;
                case BAND_STOP:
                    if (k >= k2 && k <= k4) {
                        Hk[k] = 0;
                    } else {
                        Hk[k] = 1;
                    }
                    break;
            }
            if (k > 0) {
                Hk[M - k] = Hk[k];
            }
        }

//        for (int k = 0; k <= (M - 1) / 2; k++) {
//            wx = 2 * k / (double) M;
//            //           
//            switch (typeSelect) {
//                case BAND_PASS:
//                    if (wx > ws && wx < w2s) {
//                        Hk[k] = 1;
//                    } else {
//                        Hk[k] = 0;
//                    }
//                    break;
//                case BAND_STOP:
//                    if (wx >= ws && wx <= w2s) {
//                        Hk[k] = 0;
//                    } else {
//                        Hk[k] = 1;
//                    }
//                    break;
//            }
//            if (k > 0) {
//                Hk[M - k] = Hk[k];
//            }
//        }

        wp *= pi;
        ws *= pi;
        w2s *= pi;
        w2p *= pi;

        if (typeFrequence == OPTIMUM_DESIGN) {
            findKMaxMin();
//            System.out.println("kMax= " + kMax);
//            System.out.println("kMin= " + kMin);

            if (T1 > 0 && T1 < 1 && (T2 == 0)) {
                Hk[kMax] = T1;
                Hk[kMin] = T1;
                Hk[M - kMax] = T1;
                Hk[M - kMin] = T1;
            } else if ((T1 > 0 && T1 < 1) && (T2 > 0 && T2 < 1)) {
                Hk[kMin] = T1;
                Hk[kMax] = T1;
                Hk[M - kMin] = T1;
                Hk[M - kMax] = T1;
                Hk[kMax - 1] = T2;
                Hk[kMin + 1] = T2;
                Hk[M - kMax + 1] = T2;
                Hk[M - kMin - 1] = T2;
            }
        }

        for (int k = 0; k < M; k++) {
            System.out.println("Hk[" + k + "]" + Hk[k]);

        }
        setValuehn();
        setValueDB(M);
        findMaxMin();
    }

    public void setValuehn() {
        FileWriter fstream = null;
        try {
            fstream = new FileWriter("D://FIR Frequence Sample.txt");
        } catch (IOException ex) {
            Logger.getLogger(FIRFrequenceSample.class.getName()).log(Level.SEVERE, null, ex);
        }
        BufferedWriter out = new BufferedWriter(fstream);
        double real = 0;
        double imag = 0;
        wk = new double[M];
        for (int n = 0; n < M; n++) {
            for (int k = 0; k < M; k++) {
                if (k <= (M - 1) / 2) {
                    wk[k] = -alpha * pi2 * k / M;
                } else {
                    wk[k] = alpha * pi2 * (M - k) / M;
                }
                real1 = cos(wk[k]);
                imag1 = sin(wk[k]);
                real2 = cos(pi2 * n * k / M);
                imag2 = sin(pi2 * n * k / M);
                real += Hk[k] * (real1 * real2 - imag1 * imag2);
                imag += Hk[k] * (real1 * imag2 + imag1 * real2);
            }
            real = real / M;
            imag = imag / M;

            hn[n] = module(real, imag);
            if (real < 0) {
                hn[n] = -hn[n];
            }
//            System.out.println("hn[" + n + "]= " + hn[n]);
            try {
                out.write("\nhn[" + n + "]= " + hn[n]);
            } catch (IOException ex) {
                Logger.getLogger(FIRFrequenceSample.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        try {
            out.close();
        } catch (IOException ex) {
            Logger.getLogger(FIRFrequenceSample.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void setValueDB(int M) {
        double real = 0;
        double imag = 0;
        for (int w = 0; w < 300; w++) {
            real = 0;
            imag = 0;
            for (int n = 0; n < M; n++) {
                real += hn[n] * cos(w * n * pi / 300);
                imag += hn[n] * sin(w * n * pi / 300);
            }

            Hw[w] = module(real, imag);
            HwMax = max(HwMax, Hw[w]);
        }
        for (int w = 0; w < 300; w++) {
            dB[w] = -20 * log10(Hw[w] / HwMax);
            dBMax = max(dBMax, dB[w]);
        }
        dBMin = dBMax;
        int tempW = 0;
        int tempW2 = 0;
        switch (typeSelect) {
            case LOW_PASS:
                tempW = (int) (ws * 300);
                for (int i = tempW; i < 300; i++) {
                    dBMin = min(dBMin, dB[i]);
                }
                break;
            case HIGH_PASS:
            case BAND_PASS:
                tempW = (int) (ws * 300);
                for (int i = 0; i < tempW; i++) {
                    dBMin = min(dBMin, dB[i]);
                }
                break;
            case BAND_STOP:
                tempW = (int) (ws * 300);
                tempW2 = (int) (w2s * 300);
                for (int i = tempW; i < tempW2; i++) {
                    dBMin = min(dBMin, dB[i]);
                }
                break;
        }
        System.out.println("dBMax= " + dBMax);
        System.out.println("dBMin= " + dBMin);
    }

    public void findMaxMin() {
        for (int i = 0; i < M; i++) {
            hnMax = max(hnMax, hn[i]);
            hnMin = min(hnMin, hn[i]);
        }
        for (int w = 0; w < 300; w++) {
            HwMax = max(HwMax, Hw[w]);
            HwMin = min(HwMin, Hw[w]);
        }
    }

    public void findKMaxMin() {
        kMin = (M - 1) / 2;
        kMax = 0;
        for (int k = 0; k <= (M - 1) / 2; k++) {
            switch (typeSelect) {
                case LOW_PASS:
                    if (Hk[k] == 1) {
                        kMax = max(kMax, k);
                    }
                    break;
                case HIGH_PASS:
                    if (Hk[k] == 1) {
                        kMin = min(kMin, k);
                    }
                    break;
                case BAND_PASS:
                    if (Hk[k] == 1) {
                        kMax = max(kMax, k);
                        kMin = min(kMin, k);
                    }
                    break;
                case BAND_STOP:
                    if (Hk[k] == 0) {
                        kMax = max(kMax, k);
                        kMin = min(kMin, k);
                    }
                    break;
            }
        }
    }

    public void contructArrayH(int M) {
        hn = new double[M];
        Hk = new double[M];
        Hw = new double[300];
        dB = new double[300];
    }

    int max(int x, int y) {
        return Math.max(x, y);
    }

    int min(int x, int y) {
        return Math.min(x, y);
    }

    double max(double x, double y) {
        return Math.max(x, y);
    }

    double min(double x, double y) {
        return Math.min(x, y);
    }

    double sin(double x) {
        return Math.sin(x);
    }

    double cos(double x) {
        return Math.cos(x);
    }

    double log10(double x) {
        return Math.log10(x);
    }

    double sqrt(double x) {
        return Math.sqrt(x);
    }

    double module(double real, double imag) {
        return sqrt(real * real + imag * imag);
    }

    double pow_1(int x) {
        if (x % 2 == 0) {
            return 1;
        } else {
            return -1;
        }
    }

    double abs(double x) {
        return Math.abs(x);
    }

    double pow2(double x) {
        return x * x;
    }

    int round(double x) {
        return (int) Math.round(x);
    }
}
